package com.insight.modules.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.yuanqiao.insight.common.util.common.LocalCacheUtils;
import com.yuanqiao.insight.common.util.common.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationToken;
import com.insight.common.api.CommonAPI;
import com.insight.common.api.vo.Result;
import com.insight.common.constant.CommonConstant;
import com.insight.common.system.vo.LoginUser;
import com.insight.common.util.PasswordUtil;
import com.insight.config.shiro.oauth2.ISsoLoginService;
import com.insight.config.shiro.token.JwtToken;
import com.insight.config.shiro.token.OAuth2Token;
import com.insight.modules.system.entity.SysRole;
import com.insight.modules.system.entity.SysUsers;
import com.insight.modules.system.entity.UtlSystemInfo;
import com.insight.modules.system.mapper.SysRoleMapper;
import com.insight.modules.system.service.ISysUserService;
import com.insight.modules.system.service.IUtlSystemInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Slf4j
@Service
public class SsoLoginServiceImpl implements ISsoLoginService {

    @Autowired
    private ISysUserService sysUserService;       // 用户服务接口，操作用户相关业务
    @Autowired
    private RedisUtils redisUtil;                  // Redis 工具类，用于缓存token等
    @Autowired
    private CommonAPI commonAPI;                    // 通用API，用于获取权限数据
    @Autowired
    private IUtlSystemInfoService systemInfoService; // 系统信息服务，存储配置信息
    @Autowired
    private SysRoleMapper roleMapper;              // 角色Mapper，用于数据库操作角色表

    private static final LocalCacheUtils cacheUtils = LocalCacheUtils.getInstance(); // 本地缓存工具

    /**
     * 根据用户名查找登录用户信息
     * @param username 用户名
     * @return 登录用户对象
     */
    @Override
    public LoginUser findUserByUsername(String username) {
        return sysUserService.getUsersByLoginUser(username);
    }

    /**
     * 首次登录时创建用户
     * @param token 登录令牌
     * @param userInfo 用户信息（JSON对象）
     * @return 创建后的登录用户信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginUser createUser(String token, JSONObject userInfo) {
        String userName = userInfo.getStr("user_name");
        log.info("首次登录创建用户:{}", userName);

        // 根据传入信息构建新用户实体
        SysUsers user = SysUsers.buildUser(userName,
                userInfo.getStr("password"),
                userInfo.getStr("nickname"),
                userInfo.getStr("phone"));

        // 获取默认角色配置
        com.alibaba.fastjson.JSONObject defaultRole = JSON.parseObject(
                String.valueOf(Optional.ofNullable(cacheUtils.getValue("oauth2_defaultRole")).orElse("{}"))
        );
        String roleCode = defaultRole.getString(userInfo.getStr(CommonConstant.PLATFORM_TYPE));

        // 查询默认角色信息
        SysRole sysRole = roleMapper.selectOne(new LambdaQueryWrapper<SysRole>()
                .eq(SysRole::getRoleCode, roleCode));

        // 给新用户分配默认角色
        sysUserService.addUserWithRole(user, sysRole.getId());

        // 同步权限菜单给新用户
        synMenu(token);

        // 将用户实体属性复制到登录用户对象返回
        LoginUser loginUser = new LoginUser();
        BeanUtil.copyProperties(user, loginUser);
        return loginUser;
    }

    /**
     * 用户SSO登录
     * 1. 校验用户是否有效
     * 2. 校验用户名、密码、昵称和手机号是否一致，若不一致则更新
     * 3. 生成token并缓存
     *
     * @param token 登录令牌
     * @param userInfo 用户信息
     * @return token（登录凭证）
     */
    @Override
    public String ssoUserLogin(String token, JSONObject userInfo) {
        String username = userInfo.getStr("user_name");
        String password = userInfo.getStr("password");
        String nickname = userInfo.getStr("nickname");
        String phone = userInfo.getStr("phone");

        // 1. 查询用户信息
        LambdaQueryWrapper<SysUsers> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUsers::getUsername, username);
        SysUsers sysUser = sysUserService.getOne(queryWrapper);

        // 2. 校验用户是否有效
        Result<com.alibaba.fastjson.JSONObject> result = sysUserService.checkUserIsEffective(sysUser);
        if (!result.isSuccess()) {
            return null;
        }

        // 3. 使用加盐加密密码，校验密码是否一致
        String userPassword = PasswordUtil.encrypt(username, password, sysUser.getSalt());

        // 4. 如果密码、昵称、手机号有变则更新数据库中的用户信息
        if (!StringUtils.equals(sysUser.getPassword(), userPassword)
                || !StringUtils.equals(sysUser.getRealname(), nickname)
                || !StringUtils.equals(sysUser.getPhone(), phone)) {
            sysUserService.update(new LambdaUpdateWrapper<SysUsers>()
                    .set(SysUsers::getPassword, userPassword)
                    .set(SysUsers::getRealname, nickname)
                    .set(SysUsers::getPhone, phone)
                    .eq(SysUsers::getUsername, username)
            );
        }

        // 5. 把token存入Redis，设置有效期为2小时
        redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token, 2 * 60 * 60);
        return token;
    }

    /**
     * 同步菜单给用户（通常是首次登录时调用）
     * 判断是否首次同步，若是则调用接口同步并标记
     */
    private void synMenu(String token) {
        // 查询系统信息中是否已同步过菜单
        int count = systemInfoService.count(new LambdaQueryWrapper<UtlSystemInfo>()
                .eq(UtlSystemInfo::getUtlKey, "isFirstSyncMenu")
                .eq(UtlSystemInfo::getUtlValue, "1"));

        // 未同步则执行同步
        if (count == 0) {
            log.info("首次同步菜单");

            // 获取权限数据（空参数表示获取全部）
            List<com.alibaba.fastjson.JSONObject> permissionData = commonAPI.getPermissionData(new ArrayList<>());

            // 获取配置的SSO同步菜单URL
            String ssoUrl = String.valueOf(Optional.ofNullable(cacheUtils.getValue("oauth2_ssoUrl")).orElse(""));
            String synMenu = String.valueOf(Optional.ofNullable(cacheUtils.getValue("oauth2_synMenu")).orElse(""));

            // 发送POST请求，同步菜单数据，带上token作为认证
            HttpUtil.createPost(String.format("%s%s", ssoUrl, synMenu))
                    .body(JSON.toJSONString(permissionData))
                    .bearerAuth(token)
                    .execute();

            // 标记已同步
            UtlSystemInfo utlSystemInfo = new UtlSystemInfo();
            utlSystemInfo.setUtlKey("isFirstSyncMenu");
            utlSystemInfo.setUtlValue("1");
            systemInfoService.saveOrUpdate(utlSystemInfo, new LambdaQueryWrapper<UtlSystemInfo>()
                    .eq(UtlSystemInfo::getUtlKey, "isFirstSyncMenu"));

        }
    }


    public static void main(String[] args) {
        AuthenticationToken token = new JwtToken("token");

        System.out.println(token instanceof OAuth2Token);
    }
}

